package com.ruibang.glass.purchase.service.impl;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruibang.glass.common.constants.Constants;
import com.ruibang.glass.common.constants.RedisKey;
import com.ruibang.glass.common.domain.camunda.BpmProcessInstanceCreate;
import com.ruibang.glass.common.domain.camunda.BpmProcessInstanceStatus;
import com.ruibang.glass.common.domain.camunda.BpmTaskApproveReq;
import com.ruibang.glass.common.domain.file.FileBusiness;
import com.ruibang.glass.common.domain.material.MaterialManagement;
import com.ruibang.glass.frequencycontrol.annotation.RedissonLock;
import com.ruibang.glass.purchase.constant.PurchaseErrorCode;
import com.ruibang.glass.purchase.domain.req.PurchaseApplicationReq;
import com.ruibang.glass.purchase.domain.resp.FrameItemData;
import com.ruibang.glass.purchase.domain.resp.OrderItemData;
import com.ruibang.glass.purchase.domain.resp.RequestItemImportData;
import com.ruibang.glass.purchase.dto.GPurchasePurchaseApplicationListDTO;
import com.ruibang.glass.purchase.entity.*;
import com.ruibang.glass.purchase.feign.CamundaApi;
import com.ruibang.glass.purchase.feign.FileApi;
import com.ruibang.glass.purchase.mapper.PurchaseApplicationListMapper;
import com.ruibang.glass.purchase.mapper.PurchasePurchaseRequestItemsMapper;
import com.ruibang.glass.purchase.service.CommonService;
import com.ruibang.glass.purchase.service.PurchaseApplicationService;
import com.ruibang.glass.purchase.service.PurchasePurchaseRequestItemsService;
import com.teaming.cloud.framework2.autoconfigure.redis.RedisSingleService;
import com.teaming.cloud.framework2.common.constant.CommonConstant;
import com.teaming.cloud.framework2.common.exception.ServiceException;
import com.teaming.cloud.framework2.common.model.ResultBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 采购申请列表 服务实现类
 * </p>
 *
 * @author mameng
 * @since 2024-03-26
 */
@Slf4j
@Service
public class PurchaseApplicationListServiceImpl extends ServiceImpl<PurchaseApplicationListMapper, PurchaseApplicationList> implements PurchaseApplicationService {

    @Autowired
    private CommonService commonService;
    @Resource
    private PurchasePurchaseRequestItemsMapper requestItemsMapper;
    @Autowired
    private PurchasePurchaseRequestItemsService requestItemsService;

    @Autowired
    private FileApi fileApi;

    @Autowired
    private CamundaApi camundaApi;

    @Autowired
    private RedisSingleService redisService;

    @Autowired
    private PurchaseApprovalListServiceImpl purchaseApprovalListService;

    private static final String PURCHASE_APPLICATION_NUMBER = "purchase_application_number";

    /**
     * @param purchaseApplicationReq
     * @return
     */
    @Override
    public IPage<PurchaseApplicationList> getPage(PurchaseApplicationReq purchaseApplicationReq) {
        IPage<PurchaseApplicationList> page = baseMapper.getPage(new Page<>(purchaseApplicationReq.getPageNo(), purchaseApplicationReq.getPageSize()), purchaseApplicationReq);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return page;
        }
        Map<Object, Object> userMap = redisService.hmget(RedisKey.USER_GROUP_TYPE);
        Map<Object, Object> departMap = redisService.hmget(RedisKey.DEPT_GROUP_TYPE);
        List<String> businessKeys = page.getRecords().stream().map(PurchaseApplicationList::getId).collect(Collectors.toList());
        Map<String, BpmProcessInstanceStatus> processMap = null;
        //获取业务流程状态
        ResultBody<List<BpmProcessInstanceStatus>> resultBody = camundaApi.queryProcessInstanceStatus(businessKeys);
        if (resultBody != null && CollectionUtils.isNotEmpty(resultBody.getData())) {
            processMap = resultBody.getData().stream().collect(Collectors.toMap(bp -> bp.getBusinessKey(), bp -> bp, (bp1, bp2) -> bp1));
        }
        for (PurchaseApplicationList purchaseApply : page.getRecords()) {
            if (processMap != null && processMap.get(purchaseApply.getId()) != null) {
                BpmProcessInstanceStatus process = processMap.get(purchaseApply.getId());
                purchaseApply.setProcessId(process.getProcessInstanceId());
                purchaseApply.setNodeName(process.getNodeName());
                if (StringUtils.isNotBlank(process.getUserId())) {
                    if (process.getUserId().indexOf(CommonConstant.SpecialSign.COMMA.getName()) != -1) {
                        String[] userIds = process.getUserId().split(CommonConstant.SpecialSign.COMMA.getName());
                        StringBuilder userName = new StringBuilder();
                        int size = userIds.length;
                        for (int i = 0; i < size; i++) {
                            String name = userMap.get(userIds[i]) == null ? "" : userMap.get(userIds[i]).toString();
                            if (i == size - 1) {
                                userName.append(name);
                            } else {
                                userName.append(name).append(CommonConstant.SpecialSign.COMMA.getName());
                            }
                        }
                        purchaseApply.setApproveUserId(process.getUserId());
                        purchaseApply.setApproveUser(userName.toString());
                    } else {
                        purchaseApply.setApproveUserId(process.getUserId());
                        purchaseApply.setApproveUser(userMap.get(process.getUserId()) == null ? "" : userMap.get(process.getUserId()).toString());
                    }

                }
            }
            if (departMap != null) {
                if (departMap.get(purchaseApply.getDeptId()) != null) {
                    purchaseApply.setDeptName((String) departMap.get(purchaseApply.getDeptId()));
                }
            }
        }
        return page;
    }


    @Override
    public IPage<PurchaseSpecialApplicationList> specialList(PurchaseApplicationReq purchaseApplicationReq) {
        IPage<PurchaseSpecialApplicationList> page = baseMapper.specialList(new Page<>(purchaseApplicationReq.getPageNo(), purchaseApplicationReq.getPageSize()), purchaseApplicationReq);
        return page;
    }

    @Override
    @RedissonLock(key = PURCHASE_APPLICATION_NUMBER, prefixKey = "lock_")
    public String getSequenceNumber() {
        String number = "";
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern(CommonConstant.DATE_FORMAT_UN_SEPARATE));
        Serializable serializable = redisService.get(PURCHASE_APPLICATION_NUMBER);
        if (serializable != null && StringUtils.isNotBlank(serializable.toString())) {
            number = serializable.toString();
        } else {
            number = baseMapper.getSequenceNumber();
        }
        if (StringUtils.isBlank(number)) {
            number = date.concat("0001");
        } else {
            String sequence = number.substring(8);
            int sequenceInt = Integer.parseInt(sequence);
            sequenceInt = sequenceInt + 1;
            String newSequence = String.valueOf(sequenceInt);
            if (newSequence.length() == CommonConstant.FIVE_INT) {
                number = date.concat("0001");
            } else {
                String fourDigits = String.format("%04d", sequenceInt); // 格式化数字为4位，不足部分用0填充
                number = date.concat(fourDigits);
            }
        }
        redisService.set(PURCHASE_APPLICATION_NUMBER, number);
        return number;
    }

    @Override
    public void addGPurchasePurchaseApplicationList(PurchaseApplicationList purchaseApply) {
        baseMapper.insert(purchaseApply);
        /**
         * 附件上传
         */
        if (CollectionUtils.isNotEmpty(purchaseApply.getFileIds())) {
            FileBusiness fileBusiness = new FileBusiness();
            fileBusiness.setFileIds(purchaseApply.getFileIds());
            fileBusiness.setBusinessId(purchaseApply.getId());
            fileApi.updateBusinessById(fileBusiness);
        }
        if (CollectionUtils.isNotEmpty(purchaseApply.getPurchasePurchaseRequestItemsList())) {
            List<PurchaseRequestItems> gPurchasePurchaseRequestItemsList = purchaseApply.getPurchasePurchaseRequestItemsList();
            for (PurchaseRequestItems gPurchasePurchaseRequestItems : gPurchasePurchaseRequestItemsList) {
                gPurchasePurchaseRequestItems.setPurchaseApplicationId(purchaseApply.getId());
                requestItemsMapper.insert(gPurchasePurchaseRequestItems);
            }
        }
    }

    @Override
    public void approveProcess(BpmProcessInstanceCreate instanceCreate) {
        PurchaseApplicationList application = this.getById(instanceCreate.getBusinessKey());
        if (application != null && StringUtils.isNotBlank(application.getApplicantId())) {
            //发起流程
            if (StringUtils.isNotBlank(application.getApplicantId())) {
                Map<String, Object> variables = new HashMap<>();
                variables.put(Constants.PROCESS_INSTANCE_STARTER_USER_ID, application.getApplicantId());
                variables.put(Constants.PROCESS_INSTANCE_BUSINESS_KEY, instanceCreate.getBusinessKey());
                variables.put(Constants.PROCESS_INSTANCE_MENU_ID, instanceCreate.getMenuId());
                instanceCreate.setVariables(variables);
            }
            String processId = commonService.startProcess(instanceCreate);
            //直接通过第一流程节点
            if (StringUtils.isNotBlank(processId)) {
                BpmTaskApproveReq bpmTaskApproveReq = new BpmTaskApproveReq();
                bpmTaskApproveReq.setId(processId);
                bpmTaskApproveReq.setUserId(application.getApplicantId());
                camundaApi.approve(bpmTaskApproveReq);
            }
        } else {
            throw new ServiceException(PurchaseErrorCode.PURCHASE_APPLICATION_IS_NULL.getCode(), PurchaseErrorCode.PURCHASE_APPLICATION_IS_NULL.getMsg());
        }

        //更新审批状态
        this.update(Wrappers.<PurchaseApplicationList>lambdaUpdate().eq(PurchaseApplicationList::getId, application.getApplicantId())
                .set(PurchaseApplicationList::getApproveStatus, CommonConstant.TWO_STR));
    }

    /**
     * 采购申请列表修改
     *
     * @param purchaseApply
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGPurchasePurchaseApplicationList(PurchaseApplicationList purchaseApply) {
        if (CollectionUtils.isNotEmpty(purchaseApply.getPurchasePurchaseRequestItemsList())) {
            List<PurchaseRequestItems> gPurchasePurchaseRequestItemsList = purchaseApply.getPurchasePurchaseRequestItemsList();
            //判断不可有相同物品
            List<String> itemIds = gPurchasePurchaseRequestItemsList.stream()
                    .map(PurchaseRequestItems::getItemId)
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream().filter(e -> e.getValue() > 1)
                    .map(Map.Entry::getKey).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(itemIds)){
                throw new ServiceException(PurchaseErrorCode.CANNOT_HAVE_SAME_ITEM.getCode(), PurchaseErrorCode.CANNOT_HAVE_SAME_ITEM.getMsg());
            }
            //删除
            requestItemsService.deleteByAppId(purchaseApply.getId());
            //保存物品
            for (PurchaseRequestItems purchaseRequestItems : gPurchasePurchaseRequestItemsList) {
                purchaseRequestItems.setPurchaseApplicationId(purchaseApply.getId());
                purchaseRequestItems.setId(null);
            }
            requestItemsService.saveBatch(gPurchasePurchaseRequestItemsList);
//            for (PurchaseRequestItems gPurchasePurchaseRequestItems : gPurchasePurchaseRequestItemsList) {
//                if (StringUtils.isNotBlank(gPurchasePurchaseRequestItems.getId())) {
//                    requestItemsMapper.updateById(gPurchasePurchaseRequestItems);
//                } else {
//                    gPurchasePurchaseRequestItems.setPurchaseApplicationId(purchaseApply.getId());
//                    requestItemsMapper.insert(gPurchasePurchaseRequestItems);
//                }
//            }
        }

        fileApi.deleteByBusinessId(purchaseApply.getId());

        if (CollectionUtils.isNotEmpty(purchaseApply.getFileIds())) {
            FileBusiness fileBusiness = new FileBusiness();
            fileBusiness.setFileIds(purchaseApply.getFileIds());
            fileBusiness.setBusinessId(purchaseApply.getId());
            fileApi.updateBusinessById(fileBusiness);
        }

        baseMapper.updateById(purchaseApply);
    }

    /**
     * 拆分子单
     *
     * @param purchaseApproval
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addApprove(PurchaseApprovalList purchaseApproval) {
        purchaseApproval.setId(null);
        if (CollectionUtils.isNotEmpty(purchaseApproval.getPurchaseApprovalItemsList())) {
            List<String> ids = purchaseApproval.getPurchaseApprovalItemsList().stream().map(PurchaseApprovalItems::getId).collect(Collectors.toList());
            requestItemsMapper.updateRequestById(ids);
            for (PurchaseApprovalItems items : purchaseApproval.getPurchaseApprovalItemsList()) {
                items.setId(null);
            }
        }
        if (CollectionUtils.isNotEmpty(purchaseApproval.getPurchaseApprovalSupplierList())) {
            for (PurchaseApprovalSupplier purchaseApprovalSupplier : purchaseApproval.getPurchaseApprovalSupplierList()) {
                purchaseApprovalSupplier.setId(null);
            }
        }
        if (StringUtils.isBlank(purchaseApproval.getNumber())) {
            purchaseApproval.setNumber(purchaseApprovalListService.getApproveSequenceNumber());
        } else {
            purchaseApproval.setNumber(purchaseApproval.getNumber());
        }
        if (CommonConstant.THREE_STR.equals(purchaseApproval.getSpStatus())) {
            if (CollectionUtils.isEmpty(purchaseApproval.getPurchaseApprovalSupplierList())) {
                throw new ServiceException(PurchaseErrorCode.NOT_SUPPLIER_PRODUCT.getCode(), PurchaseErrorCode.NOT_SUPPLIER_PRODUCT.getMsg());
            }
        } else {
            if (CommonConstant.TWO_STR.equals(purchaseApproval.getSpStatus())) {
                if (CollectionUtils.isEmpty(purchaseApproval.getPurchaseApprovalItemsList())) {
                    throw new ServiceException(PurchaseErrorCode.NOT_NO_PRODUCT.getCode(), PurchaseErrorCode.NOT_NO_PRODUCT.getMsg());
                }
            }
        }
        purchaseApprovalListService.addPurchaseApprovalList(purchaseApproval);
    }

    /**
     * 详情
     *
     * @param id
     */
    @Override
    public PurchaseApplicationList getGPurchasePurchaseApplicationListById(String id) {
        PurchaseApplicationList gPurchasePurchaseApplicationList = baseMapper.selectById(id);
        ResultBody<List<String>> resultBody = fileApi.getFileIdsByBusinessId(id);
        if (resultBody != null && CollectionUtils.isNotEmpty(resultBody.getData())) {
            gPurchasePurchaseApplicationList.setFileIds(resultBody.getData());
        }
        List<PurchaseRequestItems> list1 = requestItemsMapper.selectList(Wrappers.<PurchaseRequestItems>lambdaQuery()
                .eq(PurchaseRequestItems::getPurchaseApplicationId, id));
        gPurchasePurchaseApplicationList.setPurchasePurchaseRequestItemsList(list1);
        return gPurchasePurchaseApplicationList;
    }


    @Override
    public List<GPurchasePurchaseApplicationListDTO> getGPurchasePurchaseApplicationListDTOList() {
        List<GPurchasePurchaseApplicationListDTO> gPurchasePurchaseApplicationListDTOS = new ArrayList<>();
        List<PurchaseApplicationList> gPurchasePurchaseApplicationLists = baseMapper.selectList(Wrappers.<PurchaseApplicationList>lambdaQuery().isNotNull(PurchaseApplicationList::getNumber));
        for (PurchaseApplicationList gPurchasePurchaseApplicationList : gPurchasePurchaseApplicationLists) {
            GPurchasePurchaseApplicationListDTO gPurchasePurchaseApplicationListDTO = GPurchasePurchaseApplicationListDTO.builder()
                    .id(gPurchasePurchaseApplicationList.getId())
                    .productName(gPurchasePurchaseApplicationList.getProductName())
                    .number(gPurchasePurchaseApplicationList.getNumber()).build();
            gPurchasePurchaseApplicationListDTOS.add(gPurchasePurchaseApplicationListDTO);
        }
        return gPurchasePurchaseApplicationListDTOS;
    }

    @Override
    public PurchaseApplicationList getByNumber(String number) {
        return baseMapper.getByNumber(number);
    }

    @Override
    public Map<String, List<RequestItemImportData>> importItemData(MultipartFile file) {
        if (file != null) {
            List<RequestItemImportData> requestItemImportDataList = new ArrayList<>();
            try {
                EasyExcelFactory.read(file.getInputStream(), RequestItemImportData.class, new ReadListener<RequestItemImportData>() {
                    @Override
                    public void invoke(RequestItemImportData data, AnalysisContext context) {
                        requestItemImportDataList.add(data);
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        log.error("解析完成：{}", requestItemImportDataList.size());
                    }
                }).sheet().doRead();
            } catch (Exception e) {
                log.error("物品信息解析失败：{}", e.getMessage());
                throw new ServiceException(PurchaseErrorCode.ITEM_ANALYSIS_FAIL.getCode(), PurchaseErrorCode.ITEM_ANALYSIS_FAIL.getMsg());
            }
            Map<String, List<RequestItemImportData>> dataMap = new LinkedHashMap<>();
            List<RequestItemImportData> successData = new ArrayList<>();
            List<RequestItemImportData> failData = new ArrayList<>();
            //填充物品信息
            if (CollectionUtils.isNotEmpty(requestItemImportDataList)) {
                //不可有相同物品
                List<String> keys = requestItemImportDataList.stream().map(e -> e.getItemName() + e.getSpecificationModel() + e.getUnit()).collect(Collectors.toList());
                List<String> filKey = keys.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                        .entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(filKey)){
                    throw new ServiceException(PurchaseErrorCode.CANNOT_HAVE_SAME_ITEM.getCode(),PurchaseErrorCode.CANNOT_HAVE_SAME_ITEM.getMsg());
                }

                List<MaterialManagement> materialList = requestItemsMapper.getMaterialInfoByItemCode();
                if (CollectionUtils.isNotEmpty(materialList)) {
                    Map<String, MaterialManagement> materialMap = materialList.stream().collect(Collectors.toMap(e -> e.getItemName() + e.getSpecificationModel() + e.getUnit(), e -> e, (eNew, eOld) -> eNew));
                    for (RequestItemImportData requestItemImportData : requestItemImportDataList) {
                        String key = requestItemImportData.getItemName() + requestItemImportData.getSpecificationModel() + requestItemImportData.getUnit();
                        MaterialManagement materialManagement = materialMap.get(key);
                        if (materialManagement != null) {
                            requestItemImportData.setItemId(materialManagement.getId());
                            requestItemImportData.setItemCode(materialManagement.getMaterialCode());
                            requestItemImportData.setWarehouseId(materialManagement.getRequiredWarehouseId());
                            requestItemImportData.setWarehouseName(materialManagement.getWarehouseName());
                            successData.add(requestItemImportData);
                        } else {
                            failData.add(requestItemImportData);
                        }
                    }
                }
            }
            dataMap.put("successData", successData);
            dataMap.put("failData", failData);
            return dataMap;
        }
        return Collections.emptyMap();
    }

    @Override
    public void getFileTemplate(HttpServletResponse response) {
        try {
            InputStream inputStream = new ClassPathResource("/template/采购申请子单导入模板.xls").getInputStream();
            ServletOutputStream outputStream = response.getOutputStream();
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("采购申请子单导入模板.xls", "UTF-8"));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            byte[] bytes = new byte[1024];
            int read;
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        } catch (Exception e) {
            throw new ServiceException(PurchaseErrorCode.ITEM_ANALYSIS_FAIL.getCode(), PurchaseErrorCode.ITEM_ANALYSIS_FAIL.getMsg());
        }
    }

    @Override
    public void exportItemData(List<RequestItemImportData> requestItemImportData, HttpServletResponse response) {
        if (CollectionUtils.isNotEmpty(requestItemImportData)) {
            try {
                EasyExcel.write(response.getOutputStream(), RequestItemImportData.class)
                        .sheet("物品").doWrite(requestItemImportData);
            } catch (IOException e) {
                log.error("数据导出失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public void exportFrameItemData(List<FrameItemData> frameItemData, HttpServletResponse response) {
        if (CollectionUtils.isNotEmpty(frameItemData)) {
            try {
                EasyExcel.write(response.getOutputStream(), FrameItemData.class)
                        .sheet("物品").doWrite(frameItemData);
            } catch (IOException e) {
                log.error("数据导出失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public void exportOrderItemData(List<OrderItemData> orderItemData, HttpServletResponse response) {
        if (CollectionUtils.isNotEmpty(orderItemData)) {
            try {
                EasyExcel.write(response.getOutputStream(), OrderItemData.class)
                        .sheet("物品").doWrite(orderItemData);
            } catch (IOException e) {
                log.error("数据导出失败：{}", e.getMessage());
            }
        }
    }

}
